﻿using GalaSoft.MvvmLight.CommandWpf;
using Mimi.Common;
using Mimi.Models.DataModels;
using Mimi.Models.UIModels;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Mimi.ViewModels
{
    public class ExpenseViewModel : BindingBase, IExpenseViewModel
    {
        #region Fields
        private IOverviewViewModel overviewViewModel;
        private IStorageManager storageManager;
        private IFilterViewModel filterViewModel;
        #endregion

        #region Properties
        private List<Expense> expenses;
        /// <summary>
        /// <see cref="ExpenseCollection"/>
        /// </summary>
        public List<Expense> ExpenseCollection
        {
            get { return expenses ?? (expenses = new List<Expense>()); }
            set
            {
                expenses = value;
            }
        }

        private ObservableCollection<UIExpense> bindingExpenses;
        /// <summary>
        /// <see cref="BindingExpenseCollection"/>
        /// </summary>
        public ObservableCollection<UIExpense> BindingExpenseCollection
        {
            get
            {
                return bindingExpenses ?? (bindingExpenses = new ObservableCollection<UIExpense>());
            }
            set
            {
                Set(ref bindingExpenses, value);
            }
        }

        private bool isDisplayAddForm = false;
        /// <summary>
        /// <see cref="IsDisplayAddForm"/>
        /// </summary>
        public bool IsDisplayAddForm
        {
            get { return isDisplayAddForm; }
            set
            {
                Set(ref isDisplayAddForm, value);
            }
        }

        private string expenseDescription;
        /// <summary>
        /// <see cref="ExpenseDescription"/>
        /// </summary>
        public string ExpenseDescription
        {
            get { return expenseDescription; }
            set
            {
                Set(ref expenseDescription, value);
            }
        }

        private DateTime? expenseDate;
        /// <summary>
        /// <see cref="ExpenseDate"/>
        /// </summary>
        public DateTime? ExpenseDate
        {
            get { return expenseDate; }
            set
            {
                Set(ref expenseDate, value);
            }
        }

        private string expenseAmount;
        /// <summary>
        /// <see cref="ExpenseAmount"/>
        /// </summary>
        public string ExpenseAmount
        {
            get { return expenseAmount; }
            set
            {
                bool isValid = CheckValidAmount(value);

                if (isValid || string.IsNullOrEmpty(value))
                {
                    if (value.StartsWith("0") && value.Length > 1)
                    {
                        Set(ref expenseAmount, value.Remove(0, 1));
                    }
                    else
                    {
                        Set(ref expenseAmount, value);
                    }
                }
                else
                {
                    Set(ref expenseAmount, "0");
                }
            }
        }

        private double totalExpense = 0;
        /// <summary>
        /// <see cref="TotalExpense"/>
        /// </summary>
        public double TotalExpense
        {
            get { return totalExpense; }
            set
            {
                Set(ref totalExpense, value);
            }
        }

        private ObservableCollection<string> purposes;
        /// <summary>
        /// <see cref="Purposes"/>
        /// </summary>
        public ObservableCollection<string> Purposes
        {
            get { return purposes ?? (purposes = new ObservableCollection<string>()); }
            set
            {
                Set(ref purposes, value);
            }
        }

        private string selectedPurposeItem;
        /// <summary>
        /// <see cref="SelectedPurposeItem"/>
        /// </summary>
        public string SelectedPurposeItem
        {
            get { return selectedPurposeItem; }
            set
            {
                Set(ref selectedPurposeItem, value);
            }
        }

        private List<Purpose> purposeCollection;
        /// <summary>
        /// Purpose Collection
        /// </summary>
        public List<Purpose> PurposeCollection
        {
            get { return purposeCollection ?? (purposeCollection = new List<Purpose>()); }
            set
            {
                purposeCollection = value;
            }
        }

        private DateTime fromDate;
        /// <summary>
        /// <see cref="FilterFromDate"/>
        /// </summary>
        public DateTime FilterFromDate
        {
            get { return fromDate; }
            set
            {
                Set(ref fromDate, value);

                //Assign value to filter model
                filterViewModel.Model.From = value;
            }
        }

        private DateTime toDate;
        /// <summary>
        /// <see cref="FilterToDate"/>
        /// </summary>
        public DateTime FilterToDate
        {
            get { return toDate; }
            set
            {
                Set(ref toDate, value);

                //Assign value to filter model
                filterViewModel.Model.To = value;
            }
        }

        private bool filterOnDate;
        /// <summary>
        /// <see cref="IsFilteringOnDate"/>
        /// </summary>
        public bool IsFilteringOnDate
        {
            get { return filterOnDate; }
            set
            {
                Set(ref filterOnDate, value);

                //Assign value to filter model
                filterViewModel.Model.IsFilteringOnDate = value;
            }
        }

        private bool filterOnBudget;
        /// <summary>
        /// <see cref="FilterOnBudget"/>
        /// </summary>
        public bool FilterOnBudget
        {
            get { return filterOnBudget; }
            set
            {
                Set(ref filterOnBudget, value);

                //Assign value to filter model
                filterViewModel.Model.IsFilteringOnBudget = value;
            }
        }

        private bool isDisplayFilteredList;
        /// <summary>
        /// <see cref="IsDisplayFilteredList"/>
        /// </summary>
        public bool IsDisplayFilteredList
        {
            get { return isDisplayFilteredList; }
            set
            {
                Set(ref isDisplayFilteredList, value);
            }
        }

        private ObservableCollection<UIExpense> filteredCollection;
        /// <summary>
        /// <see cref="FilteredCollection"/>
        /// </summary>
        public ObservableCollection<UIExpense> FilteredCollection
        {
            get { return filteredCollection ?? (filteredCollection = new ObservableCollection<UIExpense>()); }
            set
            {
                Set(ref filteredCollection, value);
            }
        }

        private string filterSelectedPurposeItem;
        /// <summary>
        /// <see cref="FilterSelectedPurposeItem"/>
        /// </summary>
        public string FilterSelectedPurposeItem
        {
            get { return filterSelectedPurposeItem; }
            set
            {
                Set(ref filterSelectedPurposeItem, value);

                //Assign value to filter model
                Purpose purpose = this.PurposeCollection.FirstOrDefault((x) => x.Description == value);

                if (purpose != null)
                {
                    filterViewModel.Model.BudgetIds.Clear();
                    filterViewModel.Model.BudgetIds.Add(purpose.PurposeId);
                }
            }
        }
        #endregion

        #region Constructors
        /// <summary>
        /// Constructors
        /// </summary>
        public ExpenseViewModel(IOverviewViewModel overviewViewModel, IStorageManager storageManager, IFilterViewModel filterViewModel)
        {
            this.overviewViewModel = overviewViewModel;
            this.storageManager = storageManager;
            this.filterViewModel = filterViewModel;

            //Set up default date
            this.ExpenseDate = DateTime.Today;
            this.FilterFromDate = DateTime.Today;
            this.FilterToDate = DateTime.Today;
        }
        #endregion

        #region Commands
        private RelayCommand displayAddForm;
        /// <summary>
        /// <see cref="DisplayAddForm"/>
        /// </summary>
        public RelayCommand DisplayAddForm
        {
            get { return displayAddForm ?? (displayAddForm = new RelayCommand(DisplayAddFormOnScreen)); }
        }

        private RelayCommand addExpenseCommand;
        /// <summary>
        /// <see cref="AddExpenseCommand"/>
        /// </summary>
        public RelayCommand AddExpenseCommand
        {
            get { return addExpenseCommand ?? (addExpenseCommand = new RelayCommand(AddExpense)); }
        }

        private RelayCommand applyFilterCommand;
        /// <summary>
        /// <see cref="ApplyFilterCommand"/>
        /// </summary>
        public RelayCommand ApplyFilterCommand
        {
            get { return applyFilterCommand ?? (applyFilterCommand = new RelayCommand(ApplyFilter)); }
        }

        private RelayCommand showAllCommand;
        /// <summary>
        /// <see cref="ShowAllCommand"/>
        /// </summary>
        public RelayCommand ShowAllCommand
        {
            get { return showAllCommand ?? (showAllCommand = new RelayCommand(RemoveFilter)); }
        }
        #endregion

        #region Methods
        /// <summary>
        /// Show add form
        /// </summary>
        private void DisplayAddFormOnScreen()
        {
            IsDisplayAddForm = true;
        }

        /// <summary>
        /// <see cref="AddExpense"/>
        /// </summary>
        public void AddExpense()
        {
            if (string.IsNullOrEmpty(this.ExpenseDescription) || this.ExpenseDate == null
                || string.IsNullOrEmpty(ExpenseAmount))
            {
                return;
            }

            //Add a item to UI
            UIExpense newUIExpense = new UIExpense();

            newUIExpense.Amount = double.Parse(this.ExpenseAmount).ToString();
            newUIExpense.CreatedDate = this.ExpenseDate.Value.ToShortDateString();
            newUIExpense.Description = this.ExpenseDescription;
            newUIExpense.Purpose = this.SelectedPurposeItem;

            this.BindingExpenseCollection.Add(newUIExpense);

            //Add item to local data
            Expense newExpense = new Expense();

            newExpense.Amount = double.Parse(this.ExpenseAmount);
            newExpense.Date = this.ExpenseDate.Value.ToLocalTime();
            newExpense.Description = this.ExpenseDescription;

            Purpose selectedItem = this.PurposeCollection.FirstOrDefault((x) => x.Description.Equals(this.SelectedPurposeItem));

            if (selectedItem != null)
            {
                newExpense.SpentOn = selectedItem;
            }

            ExpenseCollection.Add(newExpense);

            newExpense.ExpenseId = ExpenseCollection.Count.ToString("D10");
            newUIExpense.ExpenseId = newExpense.ExpenseId;

            //Re-calculate total expense
            TotalExpense += newExpense.Amount;

            //Re-calculate balance
            overviewViewModel.UpdateBalance(newExpense);

            //Save to local
            storageManager.AddExpenseData(newExpense);

            //Clear info
            this.ExpenseAmount = "0";
            this.ExpenseDate = DateTime.Today;
            this.ExpenseDescription = string.Empty;
        }

        /// <summary>
        /// Check the expense amount validation
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        private bool CheckValidAmount(string input)
        {
            double amount;

            bool result = double.TryParse(input, out amount);

            return result;
        }

        /// <summary>
        /// Calculate total expense
        /// </summary>
        /// <returns></returns>
        private void CalculateTotalExpense()
        {
            if (ExpenseCollection.Count > 0)
            {
                //Sum all expense amount
                ExpenseCollection.ForEach((x) =>
                {
                    TotalExpense += x.Amount;
                });

                TotalExpense = Math.Round(TotalExpense, 2);
            }
            else
            {
                TotalExpense = 0;
            }
        }

        /// <summary>
        /// <see cref="LoadPurposeList"/>
        /// </summary>
        /// <param name="purposeCollection"></param>
        public void LoadPurposeList(IEnumerable<Purpose> purposeCollection)
        {
            this.PurposeCollection = new List<Purpose>(purposeCollection);
            this.PurposeCollection.ForEach((x) => this.Purposes.Add(x.Description));
        }

        /// <summary>
        /// <see cref="UpdatePurposeList"/>
        /// </summary>
        /// <param name="purpose"></param>
        public void UpdatePurposeList(Purpose purposeItem)
        {
            this.PurposeCollection.Add(purposeItem);
            this.Purposes.Add(purposeItem.Description);
        }

        /// <summary>
        /// <see cref="DeletePurpose"/>
        /// </summary>
        /// <param name="purpose"></param>
        public void DeletePurpose(Purpose purposeItem)
        {
            this.PurposeCollection.Remove(purposeItem);
            this.Purposes.Remove(purposeItem.Description);
        }

        /// <summary>
        /// <see cref="LoadStoredData()"/>
        /// </summary>
        public async void LoadStoredData()
        {
            //Get local data
            IEnumerable<Expense> localData = await storageManager.QueryAllExpenseData();

            foreach (Expense expense in localData)
            {
                //Update budget info
                Purpose budget = this.PurposeCollection.FirstOrDefault((x) => x.PurposeId == expense.SpentOn.PurposeId);

                if (budget != null)
                {
                    expense.SpentOn = budget;
                }

                //Add to collections
                this.ExpenseCollection.Add(expense);
                this.BindingExpenseCollection.Add(new UIExpense(expense));

                //Update total expense
                this.TotalExpense += expense.Amount;
            }

            Debug.WriteLine("Expense completed");
        }

        /// <summary>
        /// <see cref="ApplyFilter"/>
        /// </summary>
        public void ApplyFilter()
        {
            FilteredCollection = new ObservableCollection<UIExpense>();

            this.ExpenseCollection.ForEach((x) =>
            {
                if (filterViewModel.Check(x))
                {
                    FilteredCollection.Add(new UIExpense(x));
                }
            });

            IsDisplayFilteredList = true;
        }

        /// <summary>
        /// Do not filter data
        /// </summary>
        private void RemoveFilter()
        {
            IsDisplayFilteredList = false;
        }
        #endregion
    }
}
